function solveSudoku(board) {
  let rows = new Array(9).fill().map(() => new Set())
  let columns = new Array(9).fill().map(() => new Set())
  let boxes = new Array(9).fill().map(() => new Set())
  for (let i = 0; i < 9; i++) {
    for (let j = 1; j <= 9; j++) {
      rows[i].add(j)
      columns[i].add(j)
      boxes[i].add(j)
    }
  }
  let empty = []
  for (let i = 0; i < 9; i++) {
    for (let j = 0; j < 9; j++) {
      if (board[i][j] !== '.') {
        let val = parseInt(board[i][j])
        rows[i].delete(val)
        columns[j].delete(val)
        let index = Math.floor(i / 3) * 3 + Math.floor(j / 3)
        boxes[index].delete(val)
      } else {
        empty.push([i, j])
      }
    }
  }
  const backtrack = (iter) => {
    if (iter === empty.length) {
      return true
    }
    let [i, j] = empty[iter]
    let b = Math.floor(i / 3) * 3 + Math.floor(j / 3)
    let mergeSet = new Set([...rows[i]].filter(x => columns[j].has(x)))
    mergeSet = new Set([...mergeSet].filter(x => boxes[b].has(x)))
    for (let val of mergeSet) {
      rows[i].delete(val)
      columns[j].delete(val)
      boxes[b].delete(val)
      board[i][j] = '' + val
      if (backtrack(iter + 1)) {
        return true
      }
      rows[i].add(val)
      columns[j].add(val)
      boxes[b].add(val)
    }
    return false
  }
  backtrack(0)
}